iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0

02 - JS and CSS Clock

專案簡介

第二天的目標是建置一個對應真實時間的時鐘

課程影片:JS30 02
導讀影片:Alex

初始文件

Github 檔案位置:02 - JS and CSS Clock

網頁一開始的樣子如下

可以先去看看 最後的成品

正式製作

流程

將程式的要求拆分步驟後,我們需要做的事情如下

  1. 獲取現在時間
  2. 讓指針指到現在的時間
  3. 定時旋轉

獲取現在時間

首先,我們可以利用 const now = new Date(); 函式獲取本地時間

Date 物件的詳細介紹在 MDN Web Docs - Date 這裡,裡面有詳細的使用方式

這次我們會用到的如下

  • now.getSeconds() 得到現在秒數
  • now.getMinutes() 得到現在分鐘數
  • now.getHours() 得到現在小時數

讓指針指到現在的時間

首先,我們要先利用 CSS 效果 transform: rotate(90deg); 測試指針的旋轉狀況

以下先探討原作者及 Alex 老師在指針上的 CSS 撰寫方式

原作者 CSS 做法

在原影片中將每個 .hand 直接畫為一個指針,再調整 CSS 屬性 top:50% 將指針往上移到中心以上

 .hand {
  width: 50%;
  height: 6px;
  background: black;
  position: absolute;
  top: 50%;
}

這樣做會在旋轉時延伸一個問題,如果直接以 transform: rotate(90deg); 旋轉,會是指針的中端旋轉,而不是以時鐘中心點那端為旋轉軸。

因此,我們可以使用 transform-origin: 100%; 更改旋轉軸位置為最頂點,也就是時鐘中心點

Alex 做法

此做法是直接將 .hand 設為一塊大的方塊,指針採用偽元素的方式製作,這樣在操控指針的旋轉時,就可以直接旋轉中心點和時鐘一樣的方塊,達到指針正常旋轉的效果

.hand {
  width: 100%;
  height: 100%;
  position: absolute;
  transition: all 0.05s;
  transition-timing-function: cubic-bezier(1, 1.15, 1, -0.43);
}

指針的偽元素寫法如下,主要是將指針的高設為方格的一半,再利用 transform: translate(-50%, 0%); 將指針拉高至末端停在圓中心,這樣就能達成在方格旋轉時指針能正常旋轉了。

.second-hand:after{
      content: '';
      display: block;
      position: absolute;
      height: 50%;
      width: 4px;
      background-color: black;
      left: 50%;
      bottom: 50%;
      transform: translate(-50%, 0%);
}

讓指針指到現在時間 - JS

其中會利用到的技術是 CSS 的 transform: rotate(deg) 屬性,我們將利用 JS 操控三個指針的旋轉

旋轉的方式就是選取指針元素後,以 secondHand.style.transform = rotate(deg) 的方式設定隨時間而變的 deg 值。

deg 值的轉換則是如下

  • 360 度 / 60 秒 = 6
  • 360 度 / 60 分鐘 = 6 加上 (秒 / 60) * 6 的小移動
  • 360 度 / 12 小時 = 30 加上 (分 / 60) * 6 的小移動
const hourHand = document.querySelector('.hour-hand');
const minHand = document.querySelector('.min-hand');
const secondHand = document.querySelector('.second-hand');

function setClock(){
  const now = new Date();

  let secondDeg = now.getSeconds() * 6; // (360 / 60) = 6
  let minDeg = now.getMinutes() * 6 + ((now.getSeconds() / 60) * 6); // (360 / 60) = 6
  let hourDeg = now.getHours() * 30 + ((now.getMinutes() / 60) * 30); //(360 / 12) = 30

  secondHand.style.transform = `rotate(${secondDeg}deg)`;
  minHand.style.transform = `rotate(${minDeg}deg)`;
  hourHand.style.transform = `rotate(${hourDeg}deg)`;
}

定時旋轉

定時旋轉則可以使用三種計時器,沒有說誰好誰壞,但第三種 window.requestAnimationFrame() 的方式可以避免計時器的時間與 CSS 動畫持續時間不同。

function timeoutSetClock(){
  setClock();
  setTimeout(timeoutSetClock, 1000);
}

setTimeout(timeoutSetClock, 1000);` 
setInterval(setClock, 1000);
function animationSetClock(){
  setClock();
  window.requestAnimationFrame(animationSetClock);
}

window.requestAnimationFrame(animationSetClock);

最後程式碼

    .clock-face {
      position: relative;
      width: 100%;
      height: 100%;
      transform: translateY(-3px); /* account for the height of the clock hands */
    }
    
    .clock-face:after{
      content:'';
      display: block;
      width:25px;
      height:25px;
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: white;
    }

    .hand {
      width: 100%;
      height: 100%;
      position: absolute;
      transition: all 0.05s;
      transition-timing-function: cubic-bezier(1, 1.15, 1, -0.43);
    }

    .second-hand:after{
      content: '';
      display: block;
      position: absolute;
      height: 50%;
      width: 4px;
      background-color: black;
      left: 50%;
      bottom: 50%;
      transform: translate(-50%, 0%);
    }

    .min-hand:after{
      content: '';
      display: block;
      position: absolute;
      height: 40%;
      width: 7px;
      background-color: black;
      left: 50%;
      bottom: 50%;
      transform: translate(-50%, 0%);
    }

    .hour-hand:after{
      content: '';
      display: block;
      position: absolute;
      height: 30%;
      width: 10px;
      background-color: black;
      left: 50%;
      bottom: 50%;
      transform: translate(-50%, 0%);
    }

const hourHand = document.querySelector('.hour-hand');
const minHand = document.querySelector('.min-hand');
const secondHand = document.querySelector('.second-hand');

function setClock(){
  const now = new Date();

  let secondDeg = now.getSeconds() * 6; // (360 / 60) = 6
  let minDeg = now.getMinutes() * 6 + ((now.getSeconds() / 60) * 6); // (360 / 60) = 6
  let hourDeg = now.getHours() * 30 + ((now.getMinutes() / 60) * 30); //(360 / 12) = 30

  secondHand.style.transform = `rotate(${secondDeg}deg)`;
  minHand.style.transform = `rotate(${minDeg}deg)`;
  hourHand.style.transform = `rotate(${hourDeg}deg)`;
}

function timeoutSetClock(){
  setClock();
  setTimeout(timeoutSetClock, 1000);
}

function animationSetClock(){
  setClock();
  window.requestAnimationFrame(animationSetClock);
}

setClock();
// setInterval(setClock, 1000);

// setTimeout(timeoutSetClock);

window.requestAnimationFrame(animationSetClock);

結語

以上是第二天的製作紀錄,如有錯誤或不足的地方還請多多指教 >.<

CSS + JS Clock in Vanilla JS — #JavaScript30 2/30
[Alex 宅幹嘛] 深入淺出 Javascript30 快速導覽:Day 2:CSS + JS Clock


上一篇
JS30 -> 01 - JavaScript Drum Kit
下一篇
JS30 -> 03 - CSS Variables
系列文
剛接觸前端一個月的小白 - JavaScript30 挑戰筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言